<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>es5 将方法放在原型上</title>
  <!-- 
  在JavaScript中，ES5及之前的版本中，将方法放在原型上是实现对象方法共享和继承的重要方式。通过将方法定义在构造函数的 `prototype` 属性上，可以让所有实例对象共享这些方法，从而节省内存并提高性能。
  一、原型的概念
  - 原型（Prototype）：每个JavaScript对象都有一个内部属性 `[[Prototype]]` （在现代JavaScript中可以通过 `__proto__` 访问），指向该对象的原型对象。通过原型链，子对象可以访问父对象的方法和属性。

  二、将方法放在原型上的好处
  1. 内存节省：
    - 如果将方法直接定义在构造函数内部，那么每个实例都会拥有该方法的一个副本，导致内存浪费。将方法放在原型上，所有实例共享同一个方法。

  2. 性能提升：
    - 共享方法可以减少内存占用，提高性能，尤其在创建大量实例时，性能提升更加明显。

  三、示例
  下面是一个将方法放在原型上的示例：
  function Person(name) {
      this.name = name; // 实例属性
  }

  // 将方法放在原型上
  Person.prototype.greet = function() {
      console.log('Hello, ' + this.name);
  };

  // 创建实例
  var alice = new Person('Alice');
  var bob = new Person('Bob');

  // 调用方法
  alice.greet(); // 输出: Hello, Alice
  bob.greet();   // 输出: Hello, Bob
  在这个例子中， `greet` 方法被定义在 `Person.prototype` 上，因此所有通过 `Person` 构造函数创建的实例（如 `alice` 和 `bob` ）都可以共享这个方法。

  四、继承与原型方法
  通过将方法放在原型上，还可以实现继承。子构造函数可以通过原型链继承父构造函数的方法。
  function Animal(name) {
      this.name = name;
  }

  Animal.prototype.speak = function() {
      console.log(this.name + ' makes a noise.');
  };

  function Dog(name) {
      Animal.call(this, name); // 继承属性
  }

  // 继承父类的原型
  Dog.prototype = Object.create(Animal.prototype);
  Dog.prototype.constructor = Dog; // 修正构造函数指向

  Dog.prototype.speak = function() {
      console.log(this.name + ' barks.');
  };

  var dog = new Dog('Rex');
  dog.speak(); // 输出: Rex barks.
  在这个例子中， `Dog` 构造函数通过 `Object.create(Animal.prototype)` 继承了 `Animal` 的原型，从而使得 `Dog` 实例可以访问 `Animal` 的 `speak` 方法。

  五、总结
  - 将方法放在原型上是ES5及之前版本中实现对象方法共享的常用做法。
  - 通过原型，所有实例可以共享同一个方法，节省内存并提高性能。
  - 通过原型链，可以实现继承，使得子对象能够访问父对象的方法和属性。
   -->
</head>
<body>

  <script>
     /*
    1.什么是函数的显式原型
      * 区分和对象原型区别
    2.函数的原型的作用
      * 在通过new操作创建对象时, 将这个显式原型赋值给创建出来对象的隐式原型
    3.案例Person, 将所有的函数定义放到了显式原型上
    */

    function Person(name, age) {
      this.name = name;
      this.age = age;

      // 1.方式一: 编写函数, 会创建很多个函数对象
      // this.sayName = function() {
      //   console.log(this.name);
      // };

      // this.sayAge = function() {
      //   console.log(this.age);
      // };
    }

    // 2.方式二: 将方法放在原型上
    // 当我们多个对象拥有共同的值时, 我们可以将它放到构造函数对象的显式原型
    // 由构造函数创建出来的所有对象, 都会共享这些属性
    Person.prototype.sayName = function() {
      console.log(this.name);
    };

    Person.prototype.sayAge = function() {
      console.log(this.age);
    };

    var alice = new Person('Alice', 25);
    var bob = new Person('Bob', 30);

    // 隐式原型的作用
    // 1> stu1的隐式原型是谁? Student.prototype对象
    // 2> stu1.running查找:
    //  * 先在自己身上查找, 没有找到
    //  * 去原型去查找
    alice.sayName(); // Alice
    bob.sayAge(); // 30

    // alice.__proto__.sayAge() // undefined
    // alice.prototype.sayAge() // Cannot read properties of undefined (reading 'sayAge')

    
  </script>
  
</body>
</html>